セキュリティグループの変更・追加・削除を Microsoft Teams に通知する仕組みをCloudFormationでテンプレート化してみた

セキュリティグループの変更・追加・削除を Microsoft Teams に通知する仕組みをCloudFormationでテンプレート化してみた

Clock Icon2024.07.16

こんにちは!AWS事業本部のおつまみです。

みなさん、セキュリティグループの変更・追加・削除を検知して、Teamsに通知してほしいなぁと思ったことはありますか?私はあります。

通知することで、不正や意図しない変更を迅速に把握し、対応することが可能になります。

今回は通知する仕組みをCloudFormationテンプレートで構築できるようにしたので、紹介します!

このような通知画面となります。

チームとチャネル___nse-notify___Microsoft_Teams

実装方法はこちらのブログを参考にしました。

https://dev.classmethod.jp/articles/cfn-eventbridge-teams-notify/

構成図

今回構築する構成です。
前提として、CloudTrailが既に有効化されている環境で構築します。

microsoft-teams-cloudformation-2

セキュリティグループを変更(追加・削除)してから、Teams通知するまでの流れ

  1. セキュリティグループの変更(追加・削除):
    ユーザーまたはAWSサービスがセキュリティグループに対して変更(追加・削除)を実施

  2. CloudTrailによるログ記録:
    AWS CloudTrailが、この変更をイベントとして記録

  3. EventBridgeルールのトリガー:
    設定されたEventBridgeルール(このテンプレートのEventsRuleリソース)が、CloudTrailのイベントを検知

  4. イベントデータの変換:
    EventBridgeルールのInputTransformerが、検知したイベントデータを、Teamsに送信するための適切な形式に変換

  5. API Destinationの呼び出し:
    変換されたデータが、設定されたAPI Destination(EventsApiDestinationリソース)に送信

  6. TeamsのWebhookへの送信:
    API DestinationがTeamsのWebhook URLにPOSTリクエストを送信

  7. Teamsでの通知表示:
    TeamsがWebhookからのデータを受け取り、指定されたチャンネルに通知を表示

CloudFormationテンプレートを準備

テンプレート内容は以下の通りです。

テンプレート
AWSTemplateFormatVersion: '2010-09-09'
Description: 'sg-change-resource-notify-teams'
Parameters:
  SystemPrefix:
    Type: String
  EnvPrefix:
    Type: String
    Default: test
    AllowedValues:
      - prd
      - test
      - dev
      - poc
  WebhookURL:
    Type: String
    NoEcho: true
  MentionedUserMailAddress:
    Description: [email protected]
    Type: String
    NoEcho: true
  MentionedUserName:
    Type: String

Resources:
  IAMManagedPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: EventBridgePolicyForSecurityHubNotifytoTeams
      Path: /service-role/
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - events:InvokeApiDestination
            Resource:
              - !GetAtt EventsApiDestination.Arn

  IAMRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /service-role/
      RoleName: !Sub ${SystemPrefix}-${EnvPrefix}-eventbridge-teams-api-dest-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action: sts:AssumeRole
      MaxSessionDuration: 3600
      ManagedPolicyArns:
        - !Ref IAMManagedPolicy

  EventsRule:
    Type: AWS::Events::Rule
    Properties:
      Name: !Sub ${SystemPrefix}-${EnvPrefix}-nw-notify-teams
      EventPattern:
        detail-type:
          - AWS API Call via CloudTrail
        source:
          - "aws.ec2"
        detail:
          eventSource: ["ec2.amazonaws.com"]
          eventName:
            - AuthorizeSecurityGroupIngress
            - AuthorizeSecurityGroupEgress
            - RevokeSecurityGroupIngress
            - RevokeSecurityGroupEgress
      State: ENABLED
      Targets:
        - Arn: !GetAtt EventsApiDestination.Arn
          HttpParameters:
            HeaderParameters: {}
            QueryStringParameters: {}
          Id: EventsRuleName
          InputTransformer:
            InputPathsMap:
              AwsAccountId: '$.account'
              SecuritygroupId: '$.detail.requestParameters.groupId'
              eventId: '$.id'
              time: '$.time'
              userName: '$.detail.userIdentity.sessionContext.sessionIssuer.userName'
              eventName: '$.detail.eventName'
              region: '$.region'
            InputTemplate: !Sub |
              {
                "type": "message",
                "attachments": [
                  {
                    "contentType": "application/vnd.microsoft.card.adaptive",
                    "content": {
                      "type": "AdaptiveCard",
                      "body": [
                        {
                          "type": "TextBlock",
                          "text": "\u003cat\u003e${MentionedUserName}\u003c/at\u003e \n\n# **セキュリティグループ変更通知** \n\n \n セキュリティグループ : <SecuritygroupId>が変更されたことを検知しました。\n\n 詳細は次のとおりです。\n - アカウントID : **<AwsAccountId>**\n\n- 発生時間 : **<time>(UTC)**\n\n- 変更ユーザー名 : **<userName>**\n\n- セキュリティグループID : **<SecuritygroupId>** \n\n**詳細は以下のリンクからご確認ください。(検知してからイベント履歴確認まで15分程度時間がかります。)**\n\n[CloudTrailイベントの詳細を確認する](https://<region>.console.aws.amazon.com/cloudtrailv2/home?region=<region>#/events/<eventId>)"
                        }
                      ],
                      "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
                      "version": "1.0",
                      "msteams": {
                        "width": "full",
                        "entities": [
                          {
                            "type": "mention",
                            "text": "\u003cat\u003e${MentionedUserName}\u003c/at\u003e",
                            "mentioned": {
                              "id": "${MentionedUserMailAddress}",
                              "name": "${MentionedUserName}"
                            }
                          }
                        ]
                      }
                    }
                  }
                ]
              }
          RoleArn: !GetAtt IAMRole.Arn
      EventBusName: default

  EventsConnection:
    Type: AWS::Events::Connection
    Properties:
      Name: !Sub ${SystemPrefix}-${EnvPrefix}-teams-conn
      AuthorizationType: API_KEY
      AuthParameters:
        ApiKeyAuthParameters:
          ApiKeyName: Content-Type
          ApiKeyValue: application/json

  EventsApiDestination:
    Type: AWS::Events::ApiDestination
    Properties:
      Name: !Sub ${SystemPrefix}-${EnvPrefix}-teams-api-dest
      ConnectionArn: !GetAtt EventsConnection.Arn
      InvocationEndpoint: !Ref WebhookURL
      HttpMethod: POST
      InvocationRateLimitPerSecond: 300

テンプレート作成時のポイント

  • EventBridgeのルールのEventPatternで監視対象のリソースとイベント内容を指定しています。今回はセキュリティグループの変更通知であるAuthorizeSecurityGroupIngress, AuthorizeSecurityGroupEgress, RevokeSecurityGroupIngress, RevokeSecurityGroupEgressを指定しています。

  • EventBridgeのルールのInputTransformerでTeams通知する値の取得、およびメッセージ内容を設定しています。InputPathsMapではメールに通知する値を取得するために、EventBridgeに送られてきたJSONログイベントから特定の値を変数として抽出します。セキュリティグループの変更では以下のようなJSONログイベントが送られてきます。

JSONログイベント`AuthorizeSecurityGroupIngress`
{
    "eventVersion": "1.09",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROAVIM5J54ZWHCLMVYDN:iam-user-name",
        "arn": "arn:aws:sts::123456789012:assumed-role/iam-user-name/iam-user-name",
        "accountId": "123456789012",
        "accessKeyId": "ASIAVIM5J54Z7FBYUMEV",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAVIM5J54ZWHCLMVYDN",
                "arn": "arn:aws:iam::123456789012:role/iam-user-name",
                "accountId": "123456789012",
                "userName": "iam-user-name"
            },
            "attributes": {
                "creationDate": "2024-07-16T05:48:05Z",
                "mfaAuthenticated": "true"
            }
        }
    },
    "eventTime": "2024-07-16T06:16:26Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "AuthorizeSecurityGroupIngress",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "xx.xx.xx.xx",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
    "requestParameters": {
        "groupId": "sg-06fd86ac3d58d8f4f",
        "ipPermissions": {
            "items": [
                {
                    "ipProtocol": "tcp",
                    "fromPort": 443,
                    "toPort": 443,
                    "groups": {},
                    "ipRanges": {
                        "items": [
                            {
                                "cidrIp": "xx.xx.xx.xx/32"
                            }
                        ]
                    },
                    "ipv6Ranges": {},
                    "prefixListIds": {}
                }
            ]
        }
    },
    "responseElements": {
        "requestId": "f0b9432b-5257-4f98-b1f1-c2c602952f6c",
        "_return": true,
        "securityGroupRuleSet": {
            "items": [
                {
                    "groupOwnerId": "123456789012",
                    "groupId": "sg-06fd86ac3d58d8f4f",
                    "securityGroupRuleId": "sgr-08ee681fccc6c715d",
                    "isEgress": false,
                    "ipProtocol": "tcp",
                    "fromPort": 443,
                    "toPort": 443,
                    "cidrIpv4": "xx.xx.xx.xx/32"
                }
            ]
        }
    },
    "requestID": "f0b9432b-5257-4f98-b1f1-c2c602952f6c",
    "eventID": "af1a19e5-1a90-4c9f-8ade-ae8d598922d8",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "123456789012",
    "eventCategory": "Management",
    "tlsDetails": {
        "tlsVersion": "TLSv1.3",
        "cipherSuite": "TLS_AES_128_GCM_SHA256",
        "clientProvidedHostHeader": "ec2.ap-northeast-1.amazonaws.com"
    },
    "sessionCredentialFromConsole": "true"
}

今回はTeams通知に含めたい項目として以下を入力パスに指定しました。

入力パス
AwsAccountId: '$.account'
SecuritygroupId: '$.detail.requestParameters.groupId'
eventId: '$.id'
time: '$.time'
userName: '$.detail.userIdentity.sessionContext.sessionIssuer.userName'
eventName: '$.detail.eventName'
region: '$.region'
  • InputTemplateではメッセージを設定しています。今回は箇条書きで出力されるように設定しています。表形式など他の形式にしたい場合は、下記のブログをご参考ください。
入力テンプレート
InputTemplate: !Sub |
  {
    "type": "message",
    "attachments": [
      {
        "contentType": "application/vnd.microsoft.card.adaptive",
        "content": {
          "type": "AdaptiveCard",
          "body": [
            {
              "type": "TextBlock",
              "text": "\u003cat\u003e${MentionedUserName}\u003c/at\u003e \n\n# **セキュリティグループ変更通知** \n\n \n セキュリティグループ : <SecuritygroupId>が変更されたことを検知しました。\n\n 詳細は次のとおりです。\n - アカウントID : **<AwsAccountId>**\n\n- 発生時間 : **<time>**\n\n- 変更ユーザー名 : **<userName>**\n\n- セキュリティグループID : **<SecuritygroupId>**\n\n- CloudTrailイベントID : **<eventId>**\n\n- 変更内容 : **<eventName>**\n\n\n\n**セキュリティグループの確認方法** \n\nEC2コンソール画面の左メニューから「セキュリティグループ」を選択します。\n\n検索ウィンドウで「セキュリティグループID」を入力し、セキュリティグループが正しいことを確認してください。 \n\n \n**CloudTrailイベント履歴の確認方法** \n\nCloudTrailコンソール画面の左メニューから「イベント履歴」を選択します。 \n\n検索ウィンドウで「CloudTrailイベントID」を選択し、イベント詳細が確認できます。\n\n**CloudTrailイベントへのリンク**\n\n[CloudTrailイベントの詳細を確認する](https://<region>.console.aws.amazon.com/cloudtrailv2/home?region=<region>#/events/<eventId>)"
            }
          ],
          "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
          "version": "1.0",
          "msteams": {
            "width": "full",
            "entities": [
              {
                "type": "mention",
                "text": "\u003cat\u003e${MentionedUserName}\u003c/at\u003e",
                "mentioned": {
                  "id": "${MentionedUserMailAddress}",
                  "name": "${MentionedUserName}"
                }
              }
            ]
          }
        }
      }
    ]
  }

https://dev.classmethod.jp/articles/amazon-eventbridge-microsoft-teams

いざ検証

CloudFromationでデプロイ

パラメータは、以下の値を入れます。

  • SystemPrefix
    • システム名
  • EnvPrefix
    • 環境名
  • WebhookURL
  • MentionedUserMailAddress
    • メンション先のメールアドレス
  • MentionedUserName
    • メンション先の名前

CloudFormation___ap-northeast-1

2分ほどでデプロイが完了します。

セキュリティグループを変更

設定したセキュリティグループのインバウンドルールを編集してみます。
変更して数秒後、想定通りのTeams通知が届きました!

チームとチャネル___nse-notify___Microsoft_Teams

15分ほど経過後、Teamsに記載されているCloudTrailのリンクを選択します。ここからより詳細な情報が確認できますね。

CloudTrail___ap-northeast-1

さいごに

今回はセキュリティグループが変更・追加・削除されたことを検知して、Teamsに通知させる方法をご紹介しました。

最後までお読みいただきありがとうございました! どなたかのお役に立てれば幸いです。

以上、おつまみ(@AWS11077)でした!

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.